//*************************************************************************************
/** \file task_radio.cc
 *    This file contains a class which transmits packages of data from the sensors
 *    in an AVR One-Wire weather station system. The radio task gets averages of
 *    readings which have been saved up by sensor tasks and transmits a set of
 *    averages in a burst in order to save power. 
 *
 *  Revisions:
 *    \li 12-26-2007 JRR Original file
 *    \li 12-30-2007 JRR Corrected spelling of Fahrenheit
 *    \li 01-21-2008 JRR Modified to use average wind readings
 *    \li 03-20-2009 JRR Made compatible with new sensors and global debugging
 */
//*************************************************************************************

#include <stdlib.h>
#include <avr/io.h>
#include "queue.h"						  // Queues to store up data for a while
#include "m9xstream.h"					  // MaxStream radio header plus RS232, etc.
#include "avr_1wire.h"
#include "avr_sht15.h"
#include "stl_timer.h"
#include "stl_task.h"
#include "task_wind.h"
#include "task_temperature.h"
#include "task_temphumid.h"
#include "task_battery.h"
#include "task_radio.h"


//-------------------------------------------------------------------------------------
/** This constructor creates a new radio data sending task. This task transmits data
 *  at regular intervals and also receives setup information as the unit is being
 *  started up. 
 *  @param p_radio A pointer to a radio modem object
 *  @param p_wind_task Pointer to a task which is keeping track of wind speeds
 *  @param p_temphum_task Pointer to a task which measures temperature and humidity
 *  @param p_temper_task Pointer to a task which measures temperature only
 *  @param p_batt_task Pointer to a task which controls the solar battery charger
 *  @param a_timer A reference to the task timer
 *  @param interval_time A time stamp containing the time interval between runs
 */

task_radio::task_radio (m9XStream* p_radio, task_wind* p_wind_task, 
		task_temphumid* p_temphum_task, task_temperature* p_temper_task, 
		task_battery* p_batt_task, task_timer& a_timer, time_stamp interval_time)
	: stl_task (a_timer, interval_time)
{
	// Save the pointers to the radio and the tasks which get readings; these pointers 
	// will be needed to get and send data to and from the objects in question
	the_radio = p_radio;
	the_timer = &a_timer;
	the_wind_task = p_wind_task;
	the_sht15_task = p_temphum_task;
	the_ds1822_task = p_temper_task;
	the_batt_task = p_batt_task;

// 	GLOB_DEBUG ("Radio task constructor" << endl);
}


//-------------------------------------------------------------------------------------
/** This is the function which runs when it is called by the task scheduler. It gets
 *  wind speed data from the wind task and temperature/humidity data from the SHT15
 *  task (if used). If the radio transmission time has come up, this task switches on 
 *  the radio and transmits a burst of time/temperature data. Because it can take a
 *  long time to transmit a whole set of data, each item of data is sent in one state,
 *  then this task exits and gives other tasks a chance to run. 
 *  @param state The state of the task when this run method begins running
 *  @return The state to which the task will transition, or STL_NO_TRANSITION if no
 *	  transition is called for at this time
 */

char task_radio::run (char state)
{
	static unsigned int wind_average;			// Average speed from the wind task
	static unsigned int wind_gust;				// Maximum gust speed detected
	static unsigned int wind_lull;				// Minimum wind speed detected
	static char wind_cosine;					// Wind directions from the wind task
	static char wind_sine;
	static unsigned int temperature_now = 0;	// Temperature and humidity are used
	static unsigned char humidity_now = 0;		// if the SHT15 is attached & enabled
	static char hours;							// Hours entered during setup
	static char minutes;						// Minutes entered during setup
	char cmd_char;								// Character received through radio
	#ifdef USE_CHARGE_CTL
		unsigned int battery;					// Voltage reading from battery
	#endif

	switch (state)
	{
		// State 0 configures the radio modem
		case (0):
			// Regardless of what the next state is, it needs to run very soon
			run_again_ASAP ();

			// We're not making time stamps, so go straight to data taking
// 			GLOB_DEBUG ("Radio Task Startup" << endl);
			return (11);

		// In State 11, we're accumulating data, so take readings
		case (11):
			run_again_ASAP ();

			// Ask the wind task for readings if it exists; otherwise just use zeros
			#ifdef USE_C3
				wind_average = the_wind_task->get_average_speed ();
				wind_gust = the_wind_task->get_maximum_speed ();
				wind_lull = the_wind_task->get_minimum_speed ();
				the_wind_task->get_average_direction (&wind_cosine, &wind_sine);
				the_wind_task->reset_sums ();
			#else
				wind_average = 0;
				wind_gust = 0; 
				wind_lull = 0;
				wind_cosine = 0;
				wind_sine = 0;
			#endif

			// Get readings from the temperature/humidity task, if that task exists
			#ifdef USE_SHT15
				temperature_now = the_sht15_task->get_latest_temperature ();
				humidity_now = the_sht15_task->get_latest_humidity ();
			#endif
			#ifdef USE_DS1822
				temperature_now = the_ds1822_task->get_average ();
			#endif

			#ifdef WINDY_REPEATER			// If this radio is a repeater, go right
				return (13);				// to the step where we begin transmitting
			#else
				the_radio->wake_up ();			// If not a repeater, radio has been 
// 				GLOB_DEBUG ("Awake" << endl);	// asleep, so awaken it
				return (12);
			#endif

		// State 12 is briefly passed through as the radio is awakened. There needs to
		// be a short delay after causing the radio to wake up to allow it to warm up,
		// so we pass through this state and let other tasks run for a moment. The 
		// run_again_ASAP() method is used so the task runs again after a short time
		// (millisecond-ish) rather than a long time (many seconds).  If this radio is
		// a repeater, the radio was never asleep so this state can be skipped
		case (12):
			run_again_ASAP ();
			return (13);

		// In state 13, transmit a start of packet code and this radio's address
		case (13):
			*the_radio << "^ " << hex 
				<< (unsigned char)(the_radio->get_my_address () & 0x00FF) << dec; 

			run_again_ASAP ();				// Run the next state very soon
			return (14);

		// State 14 transmits the speed sensor's wind speed data
		case (14):
			*the_radio << WX_SEPARATOR << wind_average << WX_SEPARATOR 
				<< wind_gust << WX_SEPARATOR << wind_lull;

			run_again_ASAP ();				// Run the next state very soon
			return (15);

		// State 15 transmits wind direction data
		case (15):
			*the_radio << WX_SEPARATOR << wind_cosine << WX_SEPARATOR << wind_sine; 

			run_again_ASAP ();					  // Run the next state very soon
			#if (defined USE_SHT15 || defined USE_DS1822)
				return (16);
			#else
				return (17);
			#endif

		// If the SHT15 temperature/humidity sensor is in use, state 16 sends its data
		// and if we're using the DS1822, state 16 sends data from that
		case (16):
			*the_radio << WX_SEPARATOR <<  (temperature_now / 10) << "."
				<< (temperature_now % 10) << "F";
			#ifdef USE_SHT15
				*the_radio << WX_SEPARATOR << humidity_now;
			#endif

			run_again_ASAP ();					  // Run the next state very soon
			return (17);

		// State 17 transmits the battery voltage and/or other stuff if used
		case (17):
			#ifdef USE_CHARGE_CTL
				*the_radio << WX_SEPARATOR << *the_batt_task << "V";
			#endif

			run_again_ASAP ();					  // Run the next state very soon
			return (18);

		// State 18 finishes off the line of data with a carriage return/linefeed, then
		// waits until the serial port has finished sending the last character
		case (18):
			*the_radio << endl;

			run_again_ASAP ();					  // Run the next state very soon

			// This gives the serial port time to send the last character to the radio
			for (volatile int sleep_delay = 0; sleep_delay < 20000; sleep_delay++);
//			 if (dbg_port && dbg_port->is_sending ())
//				 return (STL_NO_TRANSITION);

			return (19);

		// State 19 happens a short time after State 18 has finished transmitting data.
		// In this state, the radio is turned off, unless this unit is a repeater, in
		// which case the radio is left on.  This state does NOT ask for the next 
		// state to run again right away, so this task won't do anything until its 
		// next run time has been reached
		case (19):
			#ifndef WINDY_REPEATER
// 				GLOB_DEBUG ("Sleep" << endl);
				for (volatile int sleep_del = 0; sleep_del < 30000; sleep_del++);
				the_radio->sleep ();
			#endif
			return (11);

		default:
			GLOB_DEBUG ("WARNING: Radio task in state " << state << endl);
			return (11);
	};

	return (STL_NO_TRANSITION);
}
